package lambdasinaction.chap10.my;

import java.util.Optional;

/**
 * Optional 类入门 汲取 Haskell 和 Scala 的灵感，Java8中引入了一个新的类 java.util.Optional<T>。 这是一个封装 Optional 值的类。
 * 举例来说，使用新的类意味着，如果你知道一个人可能有也可能没有车， 那么 Person 类内部的 car 变量就不应该声明为 Car, 遭遇某人没有车时把 null 引用赋值给它，
 * 而是应该像图10-1那样直接将其声明为 Optional<Car>类型
 *
 * 变量存在时，Optional 类只是对类简单封装。 变量不存在时，缺失的值会被建模城一个"空"的 Optional 对象， 由方法 Optional.empty()返回。
 * Optional.empty()方法是一个静态工厂方法， 它返回 Optional 类的特定单一实例。 你可能还有疑虑，null 引用和 Optional.empty()有声明本质的区别吗？
 * 从语义上，你可以把它们当作一回事儿，但是实际中它们之间的差别非常大： 如果你尝试解引用一个 null，一定会触发 NullPointerException 不过使用
 * Optional.empty()就完全没事儿， 它是 Optional 类的一个有效对象，多种场景就能调用，非常有用。 关于这一点，接下来的部分会详细介绍。
 *
 * 使用 Optional 而不是 null 的一个非常重要而又实际的语义区别是，第一个例子中， 我们在声明变量时使用的时 Optional<Car>类型，而不是 Car
 * 类型，这句声明非常清楚地表明了这里发生变量缺失是允许的。 与此相反，使用 Car 这样的类型，可能将变量赋值为 null，这意味着你需要独立面对这些， 你只能依赖你对业务模型的理解，判断一个
 * null 是否属于该变量的有效范畴。
 *
 * 创建 Optional 对象 使用 Optional 之前，你首先需要学习的是如何创建 Optional 对象。 完成这一任务有多种方法。 1.声明一个空的 Optional
 * 正如前文已经提到，你可以通过静态工厂方法 Optional.empty，创建一个空的 Optional 对象： Optional<Car> optCar = Optional.empty();
 *
 * 2.依据一个非空值创建 Optional 你还可以使用静态工厂方法 Optional.of，依据一个非空值创建一个 Optional 对象： Optional<Car> optCat =
 * Optional.of(car); 如果 car 是一个 null，这段代码会立即抛出一个 NullPointerException，而不是等到你试图访问 car 的属性值时才返回一个错误。
 *
 * 3.可接受 null 的 Optional 最后，使用静态工厂方法 Optional.ofNullable，你可以创建一个允许 null 值的 Optional 对象：
 * Optional<Car> optCar = Optional.ofNullable(car); 如果 car 是 null，那么得到的 Optional 对象就是个空对象。
 * 你可能已经猜到，我们还需要继续研究"如何获取 Optional 变量中的值"。尤其是，Optional 提供了一个 get 方法， 它能非常精准地完成这项工作，我们在后面会详细介绍这部分内容。
 * 不过 get 方法在遭遇到空的 Optional 对象时也会抛出异常，所以不按照约定的方式使用它， 又会让我们再度陷入由 null 引起的代码维护的梦魔。因此，我们首先从无需显示检查的
 * Optional 值的使用入手，这些方法与 Stream 中的某些操作极其相似。
 *
 * @author Fish
 * @since 0.0.1 2018/8/11 11:22
 */
public class MyOptional {

  /**
   * 使用 Optional 具有明显的优势。
   * 这一次，你可以用非常容易却又普适的方法实现之前你期望的效果
   * 不再需要使用那么多的条件分支，也不会增加代码的复杂性。
   * 它通过类型系统让你的域模型中隐藏的知识显式地体现在你的代码中，
   * 换句话说，你永远都不应该忘记语言的首要功能就是沟通，
   * 即使对程序设计语言而言也没有什么不同。
   * 声明方法接受一个 Optional 参数，或者将结果作为 Optional 类型返回，
   * 让你的同事或者未来你方法的使用者，很清楚地直到它可以接受空值，
   * 或者它可能返回一个空值。
   * @param person
   * @return
   */
  public String getCarInsuranceName(Optional<MyPerson> person) {
    return person.flatMap(MyPerson::getOptionalCar)
        .flatMap(MyCar::getOptionalInsurance)
        .map(MyInsurance::getName)
        .orElse("Unknown");
  }

}
